package httpsrv

import (
	"encoding/json"
	"net/http"
	"net/url"
	"sort"
	"time"

	"gitee.com/JMArch/rpcmon-gateway/storage"
	"gitee.com/JMArch/rpcmon-gateway/util"
	jwt "github.com/dgrijalva/jwt-go"
	"github.com/gorilla/websocket"
	"go.uber.org/zap"
)

func (srv *HttpSrv) RPCServiceListHandler(w http.ResponseWriter, req *http.Request) {

	defer func() {
		if p := recover(); p != nil {
			srv.log.Error("servicelist panic", zap.Any("error", p))
		}
	}()

	select {
	case <-srv.quit:
		w.WriteHeader(http.StatusServiceUnavailable)
		return
	default:
	}

	m, err := util.GetRPCServicesList(srv.conf.Rpcmon.EtcdAddr)
	if err != nil {
		srv.log.Error("get service list fail", zap.String("remote", req.RemoteAddr), zap.Error(err))
		w.WriteHeader(http.StatusServiceUnavailable)
		return
	}

	list := make([]string, 0)
	for k, _ := range m {
		list = append(list, k)
	}

	sort.Strings(list)

	cnt, err := json.Marshal(list)
	if err != nil {
		srv.log.Error("json marshal fail", zap.String("remote", req.RemoteAddr), zap.Error(err))
		w.WriteHeader(http.StatusServiceUnavailable)
		return
	}

	w.Header().Set("Access-Control-Allow-Origin", "wx-mon.int.jumei.com")
	w.WriteHeader(http.StatusOK)
	w.Write(cnt)
	return
}

func (srv *HttpSrv) QPSInfoHandler(w http.ResponseWriter, req *http.Request) {

	defer func() {
		if p := recover(); p != nil {
			srv.log.Error("qpsinfo panic", zap.Any("error", p))
		}
	}()

	select {
	case <-srv.quit:
		w.WriteHeader(http.StatusServiceUnavailable)
		return
	default:
	}

	req.ParseForm()

	tokenStr := req.Form.Get("token")
	if tokenStr == "" {
		srv.log.Error("token miss", zap.String("remote", req.RemoteAddr))
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	token, err := jwt.ParseWithClaims(tokenStr, jwt.MapClaims{},
		func(token *jwt.Token) (interface{}, error) {
			return []byte(secretKey), nil
		})

	if err != nil || token.Valid == false {
		srv.log.Error("token invalid", zap.String("remote", req.RemoteAddr), zap.Error(err))
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	service := req.Form.Get("service")

	conn, err := srv.websocket.Upgrade(w, req, nil)
	if err != nil {
		srv.log.Error("upgrade websocket fail", zap.String("remote", req.RemoteAddr), zap.Error(err))
		w.WriteHeader(http.StatusServiceUnavailable)
		return
	}
	defer conn.Close()

	b, ok := srv.conf.Rpcmon.BackendMap["qps"]
	if !ok {
		srv.log.Error("qps backend config miss", zap.String("remote", req.RemoteAddr), zap.Error(err))
		w.WriteHeader(http.StatusServiceUnavailable)
		return
	}

	u := &url.URL{
		Scheme: "ws",
		Host:   b.Addr,
		Path:   b.Path,
	}

	d := &websocket.Dialer{
		Proxy:            http.ProxyFromEnvironment,
		HandshakeTimeout: 10 * time.Second,
	}

	webConn, _, err := d.Dial(u.String(), nil)
	if err != nil {
		srv.log.Error("websocket dial fail", zap.String("remote", req.RemoteAddr), zap.Error(err))
		w.WriteHeader(http.StatusServiceUnavailable)
		return
	}
	defer webConn.Close()

	webConn.WriteMessage(websocket.TextMessage, []byte("set:json"))
	if service != "" {

		if service == "optool-service" {
			service = "Optool-service"
		} else if service == "Mobile" {
			service = "MobileService"
		} else if service == "ProductService" {
			service = "product-service"
		} else if service == "Operationservice" {
			service = "operation-service"
		} else if service == "Trusteeship" {
			service = "TrusteeshipData"
		} else if service == "openapi" {
			service = "openapi-service"
		} else if service == "wing-service" {
			service = "wingserver"
		} else if service == "erp-service" {
			service = "ERPService"
		} else if service == "koubei-service" {
			service = "Koubei"
		}
		webConn.WriteMessage(websocket.TextMessage, []byte(service))
	}

	for {
		_, buf, err := webConn.ReadMessage()
		if err != nil {
			srv.log.Error("read webConn fail", zap.String("backend", u.String()), zap.String("remote", conn.RemoteAddr().String()), zap.Error(err))
			return
		}

		if string(buf) == "[]" {
			continue
		}

		hashMap := make(map[string]int, 0)
		err = json.Unmarshal(buf, &hashMap)
		if err != nil {
			srv.log.Error("invalid message format", zap.Error(err))
			return
		} else {
			if len(hashMap) > 0 {

				allNodes := make([]storage.Node, 0)
				for k, v := range hashMap {
					node := make(storage.Node, 0)
					node["Service"] = k
					node["QPS"] = v
					allNodes = append(allNodes, node)
				}

				err = conn.WriteJSON(allNodes)
				if err != nil {
					srv.log.Error("write conn fail", zap.String("backend", u.String()), zap.String("remote", conn.RemoteAddr().String()), zap.Error(err))
					return
				}
			}
		}
	}

	return
}
